#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>        
#include <errno.h>


#define OPEN_CALL 0
#define CLOSE_CALL 1
#define WRITE_CALL 2
#define LSEEK_CALL 3
#define CLONE_CALL 4
#define ALL -1

#define CANVI_PROCES 0
#define CANVI_SYSCALL 1
#define RESET_VALORS 2
#define RESET_VALORS_ALL 3
#define ACTIVAR_SYSCALL 4
#define DESACTIVAR_SYSCALL 5

struct t_info {
    int num_entrades;
    int num_sortides_ok;
    int num_sortides_error;
    unsigned long long durada_total;
};



int main() {

    char buffer[256];
    struct t_info stats;
    int pid;
    int res0;
    int res1;
    int fdnull;
    int fd0;
    int fd1;
    int status;


    printf("\nTESTS PARA LAS LLAMADAS A SISTEMA DEL MODULO DYSPLAYER\nLlamadas para probarlo como el driver de un dispositivo\n\n");
    printf("Pulsa ENTER para iniciar los tests.");
    fflush(stdout);
    read(0, NULL, 1);
    printf("\nEjecutando tests...\n");

    fdnull = open("/dev/null", O_WRONLY, 0777);

    printf("\nTEST 1: Abrir el dispositivo.");
    fd0 = open("/dev/displayer", O_RDONLY, 0777);
    if(fd0 < 0){
        printf("----->Resultado BAD\n");
        printf("ERROR: No se puede abrir el dispositivo.\n");
        exit(0);
    }
    printf("----->Resultado OK\n");

    printf("\nTEST 2: Abrir el dispositivo otra vez, comprobando que no esta permitido.");
    fd1 = open("/dev/displayer", O_RDONLY, 0777);
    if(fd1 < 0) printf("----->Resultado OK\n");
    else {
        printf("----->Resultado BAD\n");
        printf("ERROR: Se puede abrir el dispositivo.\n\n");
        exit(0);
    }

    printf("\nTEST 3: Desactivar monitorización.");
    res0 = ioctl(fd0, DESACTIVAR_SYSCALL, ALL);
    if(res0 < 0) {
        printf("----->Resultado BAD\n");
        printf("ERROR: No se puede desactivar la monitorización.\n\n");
        exit(0);
    }
    else printf("----->Resultado OK\n");

    printf("\nTEST 4: Resetear estadísticas de todos los procesos.");
    res0 = ioctl(fd0, RESET_VALORS_ALL, 0);
    if(res0 < 0) {
        printf("----->Resultado BAD\n");
        printf("ERROR: No se puede resetear estadísticas de todos los procesos.\n\n");
        exit(0);
    }
    else printf("----->Resultado OK\n");

    printf("\nTEST 5: Activar monitorización del open y seleccionarla.");
    res0 = ioctl(fd0, ACTIVAR_SYSCALL, OPEN_CALL);
    res1 = ioctl(fd0, CANVI_SYSCALL, OPEN_CALL);
    if(res0 < 0) {
        printf("----->Resultado BAD\n");
        printf("ERROR: No se puede activar la monitorización.\n\n");
        exit(0);
    }
    else if(res1 < 0) {
        printf("----->Resultado BAD\n");
        printf("ERROR: No se puede cambiar la llamada a sistema.\n\n");
        exit(0);
    }
    else printf("----->Resultado OK\n");

    printf("\nTEST 6: Llamada a open correcta.");
    fd1 = open("newfile", O_RDWR | O_CREAT, 0777);
    read(fd0, &stats, sizeof(struct t_info));
    if(fd1 < 0) {
        printf("----->Resultado BAD\n");
        printf("ERROR: No se puede crear el fichero.\n\n");
        exit(0);
    }
    else if(stats.num_entrades != 1 || stats.num_sortides_ok != 1 || stats.num_sortides_error != 0) {
        printf("----->Resultado BAD\n");
        printf("ERROR: Las estadísticas no coinciden con los resultados esperados.\n\n");
        exit(0);
    }
    else printf("----->Resultado OK\n");

    printf("\nTEST 7: Activar monitorización del close y seleccionarla.");
    res0 = ioctl(fd0, ACTIVAR_SYSCALL, CLOSE_CALL);
    res1 = ioctl(fd0, CANVI_SYSCALL, CLOSE_CALL);
    if(res0 < 0) {
        printf("----->Resultado BAD\n");
        printf("ERROR: Can not activate the monitorization.\n\n");
        exit(0);
    }
    else if(res1 < 0) {
        printf("----->Resultado BAD\n");
        printf("ERROR: Can not switch the syscall.\n\n");
        exit(0);
    }
    else printf("----->Resultado OK\n");

    printf("\nTEST 8: Llamada a close correcta.");
    res0 = close(fd1);
    read(fd0, &stats, sizeof(struct t_info));
    if(res0 < 0) {
        printf("----->Resultado BAD\n");
        printf("ERROR: Can not close the fd.\n\n");
        exit(0);
    }
    else if(stats.num_entrades != 1 || stats.num_sortides_ok != 1 || stats.num_sortides_error != 0) {
        printf("----->Resultado BAD\n");
        printf("ERROR: Stats do not match the Resultados.\n\n");
	printf("totalcalls=%d  |okcalls=%d  |errorcalls=%d  \n",stats.num_entrades,stats.num_sortides_ok,stats.num_sortides_error);
        exit(0);
    }
    else printf("----->Resultado OK\n");
    unlink("newfile");

    printf("\nTEST 9: Resetear las estadísticas del proceso actual.");
    res0 = ioctl(fd0, RESET_VALORS, 0);
    read(fd0, &stats, sizeof(struct t_info));
    if(res0 < 0) {
        printf("----->Resultado BAD\n");
        printf("ERROR: Can not RESET_VALORS the statistics of current process\n\n");
        exit(0);
    }
    else if(stats.num_entrades != 0 || stats.num_sortides_ok != 0 || stats.num_sortides_error != 0) {
        printf("----->Resultado BAD\n");
        printf("ERROR: Stats do not match the Resultados.\n\n");
        exit(0);
    }
    else printf("----->Resultado OK\n");

    printf("\nTEST 10: Activar monitorización del write y seleccionarla.");
    res0 = ioctl(fd0, ACTIVAR_SYSCALL, WRITE_CALL);
    res1 = ioctl(fd0, CANVI_SYSCALL, WRITE_CALL);
    if(res0 < 0) {
        printf("----->Resultado BAD\n");
        printf("ERROR: Can not activate the monitorization.\n\n");
        exit(0);
    }
    else if(res1 < 0) {
        printf("----->Resultado BAD\n");
        printf("ERROR: Can not switch the syscall.\n\n");
        exit(0);
    }
    else printf("----->Resultado OK\n");

    printf("\nTEST 11: Tres llamadas a write correctas.");
    write(fdnull, "WRITE", 5);
    write(fdnull, "WRITE", 5);
    write(fdnull, "WRITE", 5);
    read(fd0, &stats, sizeof(struct t_info));
    if(stats.num_entrades != 5 || stats.num_sortides_ok != 5 || stats.num_sortides_error != 0) {
        printf("----->Resultado BAD\n");
        printf("ERROR: Stats do not match the Resultados.\n\n");
        exit(0);
    }
    else printf("----->Resultado OK\n");

    printf("\nTEST 12: Crear un proceso hijo que haga 3 opens incorrectos.");
    fflush(stdout);
    pid = fork();
    if(pid == 0) {
        pid = getpid();
        fd1 = open("newfile", O_RDWR, 0777);
        fd1 = open("newfile", O_RDWR, 0777);
        fd1 = open("newfile", O_RDWR, 0777);
        res0 = ioctl(fd0, CANVI_PROCES, &pid);
        res1 = ioctl(fd0, CANVI_SYSCALL, OPEN_CALL);
        read(fd0, &stats, sizeof(struct t_info));
        if(res0 < 0) {
            printf("----->Resultado BAD\n");
            printf("ERROR: Can not switch the process.\n\n");
            exit(0);
        }
        else if(res1 < 0) {
            printf("----->Resultado BAD\n");
            printf("ERROR: Can not switch the syscall.\n\n");
            exit(0);
        }
        else if(stats.num_entrades != 3 || stats.num_sortides_ok != 0 || stats.num_sortides_error != 3) {
            printf("----->Resultado BAD\n");
            printf("ERROR: Stats do not match the Resultados.\n\n");
            exit(0);
        }
        else printf("----->Resultado OK\n");


        printf("\nTEST 13: Hacer un reset general de las estadísticas y comprobar los resultados.");
        res0 = ioctl(fd0, RESET_VALORS_ALL, 0);
        read(fd0, &stats, sizeof(struct t_info));
        if(res0 < 0) {
            printf("----->Resultado BAD\n");
            printf("ERROR: Can not RESET_VALORS the statistics of all processes\n\n");
        }
        else if(stats.num_entrades != 0 || stats.num_sortides_ok != 0 || stats.num_sortides_error != 0) {
            printf("----->Resultado BAD\n");
            printf("ERROR: Stats do not match the Resultados.\n\n");
        }
        else printf("----->Resultado OK\n");
        exit(0);  
	
    }
    wait(pid,&status);

    printf("\nTEST 14: Cambiar el proceso monitorizado y comprobar el reset de estadísticas.");
    res0 = ioctl(fd0, CANVI_PROCES, NULL);
    read(fd0, &stats, sizeof(struct t_info));
    if(res0 < 0) {
        printf("----->Resultado BAD\n");
        printf("ERROR: Can not switch the process.\n\n");
        exit(0);
    }
    else if(stats.num_entrades != 0 || stats.num_sortides_ok != 0 || stats.num_sortides_error != 0) {
        printf("----->Resultado BAD\n");
        printf("ERROR: Stats do not match the Resultados.\n\n");
    }
    else printf("------>Resultado OK\n");
   

    printf("\nTEST 15: Activar monitorización del lseek y seleccionarla.");
    res0 = ioctl(fd0, ACTIVAR_SYSCALL, LSEEK_CALL);
    res1 = ioctl(fd0, CANVI_SYSCALL, LSEEK_CALL);
    if(res0 < 0) {
        printf("----->Resultado BAD\n");
        printf("ERROR: Can not activate the monitorization.\n\n");
        exit(0);
    }
    else if(res1 < 0) {
        printf("----->Resultado BAD\n");
        printf("ERROR: Can not switch the syscall.\n\n");
        exit(0);
    }
    else printf("------>Resultado OK\n");

    printf("\nTEST 16: Abrir un fichero y hacer tres llamadas correctas a lseek.");
    fd1 = open("newfile", O_RDWR | O_CREAT, 0777);
    lseek(fd1,1,SEEK_SET);
    lseek(fd1,2,SEEK_SET);
    lseek(fd1,5,SEEK_SET);
    read(fd0, &stats, sizeof(struct t_info));
    if(fd1 < 0) {
        printf("----->Resultado BAD\n");
        printf("ERROR: Can not create a file.\n\n");
        exit(0);
    }
    else if(stats.num_entrades != 3 || stats.num_sortides_ok != 3 || stats.num_sortides_error != 0) {
        printf("----->Resultado BAD\n");
        printf("ERROR: Stats do not match the Resultados.\n\n");
        exit(0);
    }
    else printf("----->Resultado OK\n");
    close(fd1);
    unlink("newfile");


    printf("\nTODOS LOS TESTS PASADOS\n");

    close(fd0);
    exit(0);

}
